﻿using System.Collections.Generic;
using System.Linq;
using DigitalRune.Animation.Character;
using DigitalRune.Geometry;
using DigitalRune.Mathematics.Algebra;
using DigitalRune.Physics;
using DigitalRune.Physics.Constraints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
//using Microsoft.Research.Kinect.Nui;
using Microsoft.Kinect;
using System;
using Microsoft.Speech;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;
using Microsoft.Speech.Synthesis;
using KinectAnimationSample;

using System.Windows;
using ShapeGame.Speech;

using System.Drawing;
using System.Windows.Forms;

namespace KinectAnimationSample
{
  // This sample animates the Dude character model using Kinect data.
  // To animate the Dude, a "Marionette" approach is used: A ragdoll is created and the 
  // important joints (hands, elbows, neck, knees, ankles) are pulled to the target positions 
  // using BallJoint constraints.
  // The pelvis body is kinematic and is positioned directly at the Kinect HipCenter position.

   

  public class KinectAnimationSample : Sample
  {
    // A scale factor that is used to scale the Kinect data to make the size of the Kinect
    // skeleton similar to the Dude skeleton. 
    // If you use a different model or if you are considerably taller, then you might have to
    // tweak this scale factor.

      

    private static readonly Vector3F _kinectScale = new Vector3F(2, 2, 2);
    
    private BasicEffect _basicEffect;

    // The Kinect device.
    private KinectSensor _kinect;

    // A SkeletonPose that contains the joint positions of the Kinect skeleton data.
    // This skeleton is a different from usual skeletons. Normally, most bone transformations
    // contain only rotation and no translations. But the Kinect skeleton data consists only of
    // translations (= joint positions) but no rotations! This SkeletonPose is mainly used
    // to store the joint positions and to let SkeletonHelper.DrawBones draw the model. It would
    // be more efficient to use a simple list for the Kinect skeleton joint positions.
    private SkeletonPose _kinectSkeletonPose;

    // The Dude character model.
    private Model _model;
    // The ragdoll of the Dude model.
    private Ragdoll _ragdoll; 
    // A skeleton that is used to animate the Dude. This skeleton is updated by the ragdoll bodies.
    private SkeletonPose _ragdollSkeletonPose;
    
    // BallJoints constraints that pull important joints of the ragdoll to the target positions
    // All these joints will use a small MaxForce because these constraints should be weaker than 
    // the normal ragdoll joints and limits.
    private BallJoint _neckSpring;
    private BallJoint _elbowLeftSpring;
    private BallJoint _handLeftSpring;
    private BallJoint _elbowRightSpring;
    private BallJoint _handRightSpring;
    private BallJoint _kneeLeftSpring;
    private BallJoint _ankleLeftSpring;
    private BallJoint _kneeRightSpring;
    private BallJoint _ankleRightSpring;

    //for speech

    SpeechRecognizer mySpeechRecognizer;

    private Game1 gameCls;

    public KinectAnimationSample(Game game)
      : base(game)
    {
        gameCls = (Game1)game;
      DisplayMessage = @"KinectAnimationSample
The right skeleton shows the Kinect skeleton data (if a player was recognized).
The left model is animated using the Kinect data.";
    }


    protected override void LoadContent()
    {
      _basicEffect = new BasicEffect(GraphicsDevice);
      InitializeKinect();
      InitializeDude();      
      InitializeAnimationConstraints();
      _spriteBatch = new SpriteBatch(GraphicsDevice);
      _spriteFont = Game.Content.Load<SpriteFont>("SpriteFont1");

      base.LoadContent();
    }


    private void InitializeKinect()
    {
      // Create a list of the bone/joint names of the Kinect skeleton data.
      var boneNames = new string[20];
      for (int i = 0; i < 20; i++)
        boneNames[i] = ((JointType)i).ToString();

      // Create list with one entry per bone. Each entry is the index of the parent bone.
      var boneParents = new[]
      {
        -1,
        (int)JointType.HipCenter,
        (int)JointType.Spine,
        (int)JointType.ShoulderCenter,
        (int)JointType.ShoulderCenter,
        (int)JointType.ShoulderLeft,
        (int)JointType.ElbowLeft,
        (int)JointType.WristLeft,
        (int)JointType.ShoulderCenter,
        (int)JointType.ShoulderRight,
        (int)JointType.ElbowRight,
        (int)JointType.WristRight,
        (int)JointType.HipCenter,
        (int)JointType.HipLeft,
        (int)JointType.KneeLeft,
        (int)JointType.AnkleLeft,
        (int)JointType.HipCenter,
        (int)JointType.HipRight,
        (int)JointType.KneeRight,
        (int)JointType.AnkleRight,
      };

      // Create a list of the bind pose transformations of all bones. Since we do not 
      // get such information from Kinect, we position all bones in the local origin.
      var boneBindPoses = new SrtTransform[20];
      for (int i = 0; i <20; i++)
        boneBindPoses[i] = SrtTransform.Identity;

      // Create a SkeletonPose that will hold the Kinect joint data.
      var skeleton = new DigitalRune.Animation.Character.Skeleton(boneParents, boneNames, boneBindPoses);
      _kinectSkeletonPose = SkeletonPose.Create(skeleton);

      // Start the Kinect device.
      _kinect = KinectSensor.KinectSensors[0];
      //_kinect.Initialize(RuntimeOptions.UseSkeletalTracking);
      //_kinect.SkeletonEngine.TransformSmooth = true;
      _kinect.SkeletonStream.Enable();
      _kinect.Start();
      _kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(OnSkeletonFrameReady);

      this.mySpeechRecognizer = SpeechRecognizer.Create();
      this.mySpeechRecognizer.SaidSomething += this.RecognizerSaidSomething;
      this.mySpeechRecognizer.Start(_kinect.AudioSource);
      //enableAec.Visibility = Visibility.Visible;
      //this.UpdateEchoCancellation(this.enableAec);
     
      
      //mySpeechRecognizer.Start(_kinect.AudioSource);
     // _kinect.SkeletonFrameReady += OnSkeletonFrameReady;
    }

    

    private void InitializeDude()
    {
      // Load dude model including the skeleton.
      _model = Game.Content.Load<Model>("Dude/m");
      var additionalData = (Dictionary<string, object>)_model.Tag;
      var ragdollSkeleton = (DigitalRune.Animation.Character.Skeleton)additionalData["Skeleton"];
      _ragdollSkeletonPose = SkeletonPose.Create(ragdollSkeleton);

      // Create a ragdoll for the Dude model.
      _ragdoll = new Ragdoll();
      DudeRagdollCreator.Create(_ragdollSkeletonPose, _ragdoll, Simulation);

      // Set the world space pose of the whole ragdoll. And copy the bone poses of the
      // current skeleton pose.
      _ragdoll.UpdateBodiesFromSkeleton(_ragdollSkeletonPose);

      // Disable sleeping.
      foreach (var body in _ragdoll.Bodies)
      {
        if (body != null)
          body.CanSleep = false;
      }

      // The pelvis bone (index 1) is updated directly from the Kinect hip center.
      _ragdoll.Bodies[1].MotionType = MotionType.Kinematic;

      // In this sample we use a passive ragdoll where we need joints to hold the
      // limbs together and limits to restrict angular movement.
      _ragdoll.EnableJoints();
      _ragdoll.EnableLimits();

      // Set all motors to constraint motors that only use damping. This adds a damping
      // effect to all ragdoll limbs.
      foreach (RagdollMotor motor in _ragdoll.Motors)
      {
        if (motor != null)
        {
          motor.Mode = RagdollMotorMode.Constraint;
          motor.ConstraintDamping = 100;
          motor.ConstraintSpring = 0;
        }
      }
      _ragdoll.EnableMotors();

      // Add rigid bodies and the constraints of the ragdoll to the simulation.
      _ragdoll.AddToSimulation(Simulation);      
    }


    private void InitializeAnimationConstraints()
    {
      // Create constraints that pull important body parts to Kinect joint positions.
      // The Update() method will update the BallJoint.AnchorPositionALocal vectors.

      // Limit the maximal forces that these joints can apply. We do not want these joints to be
      // so strong that they can violate the ragdoll joint and limit constraints.
      float maxForce = 1000;

      var ragdollSkeleton = _ragdollSkeletonPose.Skeleton;

      _neckSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("Neck")],
        MaxForce = maxForce,
      };
      Simulation.Constraints.Add(_neckSpring);
      _elbowLeftSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("L_Forearm")],
        MaxForce = maxForce / 2,    // Ellbow springs are weaker because the correct 
                                    // hand position is more important and the hand 
                                    // constraint should therefore be stronger.
      };
      // This constraint should be attached at the elbow position and not at the center of the forearm:
      var elbowLeftJointPosition = _ragdollSkeletonPose.GetBonePoseAbsolute(ragdollSkeleton.GetIndex("L_Forearm")).Translation;
      _elbowLeftSpring.AnchorPositionBLocal = _elbowLeftSpring.BodyB.Pose.ToLocalPosition(elbowLeftJointPosition);
      Simulation.Constraints.Add(_elbowLeftSpring);
      
      _handLeftSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("L_Hand")],
        MaxForce = maxForce,
      };
      Simulation.Constraints.Add(_handLeftSpring);
      
      _elbowRightSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("R_Forearm")],
        MaxForce = maxForce / 2,
      };
      // This constraint should be attached at the elbow position and not at the center of the forearm:
      var elbowRightJointPosition = _ragdollSkeletonPose.GetBonePoseAbsolute(ragdollSkeleton.GetIndex("R_Forearm")).Translation;
      _elbowRightSpring.AnchorPositionBLocal = _elbowRightSpring.BodyB.Pose.ToLocalPosition(elbowRightJointPosition);
      Simulation.Constraints.Add(_elbowRightSpring);
      
      _handRightSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("R_Hand")],
        MaxForce = maxForce,
      };
      Simulation.Constraints.Add(_handRightSpring);

      _kneeLeftSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("L_Knee2")],
        MaxForce = maxForce,
      };
      // This constraint should be attached at the knee position and not at the center of the lower leg:
      var kneeLeftJointPosition = _ragdollSkeletonPose.GetBonePoseAbsolute(ragdollSkeleton.GetIndex("L_Knee2")).Translation;
      _kneeLeftSpring.AnchorPositionBLocal = _kneeLeftSpring.BodyB.Pose.ToLocalPosition(kneeLeftJointPosition);
      Simulation.Constraints.Add(_kneeLeftSpring);
      
      _ankleLeftSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("L_Ankle1")],
        MaxForce = maxForce,
      };
      Simulation.Constraints.Add(_ankleLeftSpring);

      _kneeRightSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("R_Knee")],
        MaxForce = maxForce,
      };
      // This constraint should be attached at the knee position and not at the center of the lower leg:
      var kneeRightJointPosition = _ragdollSkeletonPose.GetBonePoseAbsolute(ragdollSkeleton.GetIndex("R_Knee")).Translation;
      _kneeRightSpring.AnchorPositionBLocal = _kneeRightSpring.BodyB.Pose.ToLocalPosition(kneeRightJointPosition);
      Simulation.Constraints.Add(_kneeRightSpring);
      
      _ankleRightSpring = new BallJoint
      {
        BodyA = Simulation.World,
        BodyB = _ragdoll.Bodies[ragdollSkeleton.GetIndex("R_Ankle")],
        MaxForce = maxForce,
      };
      Simulation.Constraints.Add(_ankleRightSpring);
    }


    Microsoft.Kinect.Skeleton[] skeletonData;
    // Called when Kinect has new skeleton data.
    private void OnSkeletonFrameReady(object sender, AllFramesReadyEventArgs e)
    {
        SkeletonFrame skeletonFrame = e.OpenSkeletonFrame();

      // This shouldn't be necessary, but if power is removed from the Kinect, you 
      // may still get an event here, but skeletonFrame will be null.
      if (skeletonFrame == null)
        return;

      // Get the first detected skeleton. 
      //var skeletonData = skeletonFrame.Skeletons.FirstOrDefault(s => s.TrackingState == SkeletonTrackingState.Tracked);
       skeletonData = new Microsoft.Kinect.Skeleton[skeletonFrame.SkeletonArrayLength];
      skeletonFrame.CopySkeletonDataTo(skeletonData);
      if (skeletonData == null)
        return;

      // The position of hip center. 
      var hipCenterPosition = GetKinectJointPosition(skeletonData, 0);

      // Get joint positions. 
      // Make all joint Z values relative to the hip center.
      // Add a hardcoded z-offset to position the skeleton above the ground.
      // And store the joint positions in the _kinectSkeletonPose.
      for (int i = 0; i < _kinectSkeletonPose.Skeleton.NumberOfBones; i++)
      {
        var position = GetKinectJointPosition(skeletonData, i) + new Vector3F(0, 1.7f, -hipCenterPosition.Z); 
        _kinectSkeletonPose.SetBonePoseAbsolute(i, new SrtTransform(QuaternionF.Identity, position));
      }
    }


    private Vector3F GetKinectJointPosition(Microsoft.Kinect.Skeleton[] skeletonData, int boneIndex)
    {
      // Convert Kinect Vector to a Vector3F and scale the Kinect skeleton.
      // X and Z are also negated to make the skeleton face the camera.
      var p = skeletonData[0].Joints[(JointType)boneIndex].Position;
      return new Vector3F(-p.X, p.Y, -p.Z) * _kinectScale;  
    }


    protected override void UnloadContent()
    {
      // Deinitialize the Kinect device.
        _kinect.Stop();

        if (this.mySpeechRecognizer != null)
        {
            this.mySpeechRecognizer.Stop();
            this.mySpeechRecognizer.SaidSomething -= this.RecognizerSaidSomething;
            this.mySpeechRecognizer.Dispose();
            this.mySpeechRecognizer = null;
        }
      //_kinect.SkeletonFrameReady -= OnSkeletonFrameReady;
      //_kinect.Uninitialize();
      _kinect = null;

      base.UnloadContent();
    }


    public  Microsoft.Kinect.Skeleton[] getskeletonData(){
        return skeletonData;
    }

    BVHExporter bvhExporter=null;
    private SpriteBatch _spriteBatch;
    private SpriteFont _spriteFont;

    private String msg="";

    private void RecognizerSaidSomething(object sender, SpeechRecognizer.SaidSomethingEventArgs e)
    {
        
        switch (e.Verb)
        {
            case  SpeechRecognizer.Verbs.Reset:
                scrren_save();
                gameCls.Reset();
                break;
            case SpeechRecognizer.Verbs.Exit:
                Game.Exit();
                break;
            case SpeechRecognizer.Verbs.Move:


                msg = "Move";
                if (bvhExporter == null)
                {
                    bvhExporter = new BVHExporter(this);
                    //bvhExporter.save();
                    bvhExporter.putHierarchy();
                    bvhExporter.startMotionCapture();
                }
                break;
            case SpeechRecognizer.Verbs.Stop:
                msg = "Stop";
                if (bvhExporter != null)
                {
                    bvhExporter.stopMotionCapture();
                    bvhExporter.save();
                    bvhExporter = null;
                    
                }
               
                break;
                
        }
    }
       public void scrren_save() {
            Bitmap screen = null;
            System.Drawing.Rectangle bounds = System.Windows.Forms.Control.FromHandle(gameCls.Window.Handle).Bounds;
            
                screen = new Bitmap(bounds.Width, bounds.Height);
                using (Graphics g = Graphics.FromImage(screen))
                {
                    g.CopyFromScreen(new System.Drawing.Point(bounds.Left, bounds.Top), System.Drawing.Point.Empty, screen.Size);
                }
                screen.Save("output.png", System.Drawing.Imaging.ImageFormat.Png);
                
            
        }

    public override void Update(GameTime gameTime)
    {
      // Update the skeleton bone transforms from the current rigid body positions.
      _ragdoll.UpdateSkeletonFromBodies(_ragdollSkeletonPose);

      // Since not all joints are constrained by the Kinect skeleton, we might want 
      // to show some bones always in a specific pose if they move too much.
      _ragdollSkeletonPose.SetBoneTransform(_ragdollSkeletonPose.Skeleton.GetIndex("Neck"), SrtTransform.Identity);

      // The new pelvis position. (We keep the original rotation and use the Kinect position.)
      var newPose = new Pose(_kinectSkeletonPose.GetBonePoseAbsolute(0).Translation) * new Pose(_ragdollSkeletonPose.Skeleton.GetBindPoseRelative(1).Rotation.Inverse);

      // If the new position is too far away from the last position, then we limit the 
      // position change. If the ragdoll makes very large jumps in a single step, then 
      // it could get tangled up.
      var oldPose = _ragdoll.Bodies[1].Pose;          // Pelvis has bone index 1.
      var translation = newPose.Position - oldPose.Position;
      var maxTranslation = 0.1f;
      if (translation.Length > maxTranslation)
      {
        translation.Length = maxTranslation;
        newPose.Position = oldPose.Position + translation;
      }
      _ragdoll.Bodies[1].Pose = newPose;

      // Update the target positions for the animation joints.
      _neckSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.Head).Translation;
      _elbowLeftSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.ElbowLeft).Translation;
      _handLeftSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.HandLeft).Translation;
      _elbowRightSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.ElbowRight).Translation;
      _handRightSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.HandRight).Translation;
      _kneeLeftSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.KneeLeft).Translation;
      _ankleLeftSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.AnkleLeft).Translation;
      _kneeRightSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.KneeRight).Translation;
      _ankleRightSpring.AnchorPositionALocal = _kinectSkeletonPose.GetBonePoseAbsolute((int)JointType.AnkleRight).Translation;

      base.Update(gameTime);
    }
    

    public override void Draw(GameTime gameTime)
    {
      // Restore render states.
      GraphicsDevice.DepthStencilState = DepthStencilState.Default;
      GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
      GraphicsDevice.BlendState = BlendState.Opaque;
      GraphicsDevice.SamplerStates[0] = SamplerState.AnisotropicWrap;
        
      // Draw green ground.
      GroundModel.Draw(Matrix.Identity, Camera.View, Camera.Projection);

      // Draw animated Dude on the left side.
      DrawSkinnedModel(_model, new Pose(new Vector3F(-1, 0, 0)) * _ragdoll.Pose, _ragdollSkeletonPose);

      // Draw Kinect skeleton for debugging on the right side.
      _basicEffect.World = Matrix.CreateTranslation(1, 0, 0);
      _basicEffect.View = Camera.View;
      _basicEffect.Projection = Camera.Projection;
      SkeletonHelper.DrawBones(_kinectSkeletonPose, GraphicsDevice, _basicEffect, 0.1f,
                               SpriteBatch, SpriteFont, Microsoft.Xna.Framework.Color.Orange);


      float left = MathHelper.Max(GraphicsDevice.Viewport.TitleSafeArea.Left, 20);
      float top = MathHelper.Max(GraphicsDevice.Viewport.TitleSafeArea.Top, 20);
      Vector2 position = new Vector2(left, top);

      

      _spriteBatch.Begin();
      //_spriteBatch.DrawString(_spriteFont, msg, position, Color.Black);
      _spriteBatch.DrawString(_spriteFont, msg, new Vector2(52, 15),
                         Microsoft.Xna.Framework.Color.Black, 0, Vector2.Zero, 5, SpriteEffects.None, 0);

      _spriteBatch.End();
               
      base.Draw(gameTime);
    }
  }
}
